﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DELVE_GAME
{

    enum UPGRADE_TYPES
    {
        SPEED,
        POTENCY,
        STATIC_POTENCY,
        OUTBREAK,
        RESILIENCE,
        BASE
    }

    enum UPGRADE_COSTS
    {
        //costs
        SPEED = 8,
        POTENCY = 6,
        POT_W_BASE = 16,
        STATIC_POTENCY = 2,
        OUTBREAK = 4,
        RESILIENCE = 8,
        BASE = 8,
    }

    enum VIRUS_DEFAULTS
    {
        NUM_UPGRADES = 6,

        SPEED = 1,
        MAX_SPEED = 4,

        BASE_POTENCY = 8,

        POTENCY_MOD = 1,
        STATIC_POTENCY_MOD = 3,
        MAX_POTENCY = 20,
    }

    public class Virus : IComparable<Virus>
    {
        bool futureBool = false;
        int playerIndex;
        int potency;
        int staticPotency; //virus potency from static potency upgrade
        int speed;

        bool outbreak;
        bool resilient;
        bool ageless;

        Tile tile;
        bool active; //Can this virus expand?

        HashSet<Tile> visibleTiles;
        List<int> allianceList;

        //***********************************************************************Rick Alert Changes*************************************************************************
        //List<Tile> underInfluence = new List<Tile>();
        //Virus myVirtualVirus;
        //***********************************************************************Rick Alert Changes*************************************************************************

        #region virusFutureCopyConstructor
        //rick virus futureCopyConstructor
        public Virus(Virus currentVirus)
        {
            futureBool = true;
            this.upgrades = new List<Upgrade>();
            foreach(Upgrade u in currentVirus.upgrades){
                this.upgrades.Add(new Upgrade(u));
            }

            this.playerIndex = currentVirus.playerIndex;
            this.potency = currentVirus.potency;
            this.staticPotency = currentVirus.staticPotency;
            this.speed = currentVirus.speed;
            this.outbreak = currentVirus.outbreak;
            this.resilient = currentVirus.resilient;
            this.ageless = currentVirus.ageless;
            this.tile = currentVirus.tile;
            this.active = currentVirus.active;
            
            this.visibleTiles = new HashSet<Tile>();
            /*foreach (Tile t in currentVirus.visibleTiles) {
                this.visibleTiles.Add(t);
            }*/

            this.allianceList = new List<int>();
            foreach (int i in currentVirus.allianceList) {
                this.allianceList.Add(i);
            }

            /*this.underInfluence = new List<Tile>();
            foreach (Tile t in currentVirus.underInfluence) {
                this.underInfluence.Add(new Tile(t));
            }

            this.myVirtualVirus = new Virus(currentVirus.myVirtualVirus);*/
        }
        #endregion


        #region upgrades


        class Upgrade
        {
            int count;
            int upgradeCost;

            public Upgrade(Upgrade u) {
                this.count = u.count;
                this.upgradeCost = u.upgradeCost;
            }
            public Upgrade(int cost) { count = 0; upgradeCost = cost; }
            public int add() { count++; return upgradeCost; }
            public int remove()
            {
                if (count > 0)
                {
                    count--;
                    return upgradeCost;
                }
                else return 0;
            }
            public int getCount() { return count;}
            public int getCost() { return upgradeCost; }
        }

        List<Upgrade> upgrades;

        public List<int> displayUpgrades(int playerPotency)
        {
            List<int> upgradeable = new List<int>();
            for (int i = 0; i < upgrades.Count; ++i)
            {
                if (isUpgradable(playerPotency, i))
                    upgradeable.Add(i);
            }
            return upgradeable;
        }

        public List<int> getStats()
        {
            List<int> myStats = new List<int>();
            for (int i = 0; i < (int)VIRUS_DEFAULTS.NUM_UPGRADES; i++)
            {
                switch (i)
                {
                    case (int)UPGRADE_TYPES.SPEED: myStats.Add(speed); break;
                    case (int)UPGRADE_TYPES.POTENCY: myStats.Add(potency); break;
                    case (int)UPGRADE_TYPES.STATIC_POTENCY: myStats.Add(staticPotency); break;
                    case (int)UPGRADE_TYPES.OUTBREAK: myStats.Add(outbreak ? 1 : 0); break;
                    case (int)UPGRADE_TYPES.RESILIENCE: myStats.Add(resilient ? 1 : 0); halfAged = false; break;
                    case (int)UPGRADE_TYPES.BASE: myStats.Add(ageless ? 1 : 0); break;
                }
            }
            return myStats;
        }

        public int addUpgrade(int upgrade)
        {
            switch (upgrade)
            {
                case (int)UPGRADE_TYPES.SPEED: speed++; break;
                case (int)UPGRADE_TYPES.POTENCY: 
                    potency += (int)VIRUS_DEFAULTS.POTENCY_MOD;
                    if(ageless) return (int) UPGRADE_COSTS.POT_W_BASE;
                    break;
                case (int)UPGRADE_TYPES.STATIC_POTENCY: staticPotency += (int)VIRUS_DEFAULTS.STATIC_POTENCY_MOD;
                    break;
                case (int)UPGRADE_TYPES.OUTBREAK: outbreak = true; break;
                case (int)UPGRADE_TYPES.RESILIENCE: resilient = true; halfAged = false; break;
                case (int)UPGRADE_TYPES.BASE:
                    //change normal square into base at potency >= BASE_POTENCY
                    //Static potency is unaffected
                    int startPotency = potency;
                    if(potency < (int) VIRUS_DEFAULTS.BASE_POTENCY)
                        potency = (int)VIRUS_DEFAULTS.BASE_POTENCY;

                    ageless = true;
                    return GET_BASE_COST(startPotency);
            }
            //Returns cost
            return upgrades[upgrade].add();
        }
        public bool isUpgradable(int playerPotency, int upgrade)
        {
            bool upgradable = true;
            switch (upgrade)
            {
                case (int)UPGRADE_TYPES.POTENCY:
                    if (getViewPotency() + (int)VIRUS_DEFAULTS.POTENCY_MOD >
                        (int)VIRUS_DEFAULTS.MAX_POTENCY) upgradable = false;

                    break;
                case (int)UPGRADE_TYPES.STATIC_POTENCY:
                    if (getViewPotency() + (int)VIRUS_DEFAULTS.STATIC_POTENCY_MOD >
                        (int)VIRUS_DEFAULTS.MAX_POTENCY) upgradable = false;
                    break;
                case (int) UPGRADE_TYPES.SPEED:
                    upgradable = !(speed == (int)VIRUS_DEFAULTS.MAX_SPEED);
                    break;
                case (int)UPGRADE_TYPES.OUTBREAK: 
                    upgradable = !outbreak;
                    break;
                case (int)UPGRADE_TYPES.RESILIENCE:
                    upgradable = !(resilient || ageless);
                    break;
                case (int)UPGRADE_TYPES.BASE: //cost of upgrading current square to base
                    upgradable = false;
                    if (!ageless && (int)VIRUS_DEFAULTS.BASE_POTENCY + staticPotency 
                        <= (int)VIRUS_DEFAULTS.MAX_POTENCY)
                            upgradable = true;
                    break;
            }
            //if upgrade can be applied to square, test cost
            return (upgradable ? (upgradeCost(upgrade) <= playerPotency) : false);
        }

        public int upgradeCost(int upgrade)
        {
            int cost = 0;
            switch (upgrade)
            {
                case (int)UPGRADE_TYPES.POTENCY:
                    cost = (ageless ? (int)UPGRADE_COSTS.POT_W_BASE : upgrades[upgrade].getCost());
                    break;
                case (int)UPGRADE_TYPES.STATIC_POTENCY:
                case (int)UPGRADE_TYPES.SPEED:
                case (int)UPGRADE_TYPES.OUTBREAK:
                case (int)UPGRADE_TYPES.RESILIENCE:
                    cost = upgrades[upgrade].getCost();
                    break;
                case (int)UPGRADE_TYPES.BASE: //cost of upgrading current square to base
                    cost = GET_BASE_COST(potency);
                    break;
            }
            return cost;
        }

        private void initializeUpgrades()
        {
            upgrades = new List<Upgrade>();
            for (int i = 0; i < (int) VIRUS_DEFAULTS.NUM_UPGRADES; ++i)
            {
                int cost = 0;
                switch (i)
                {
                    case (int)UPGRADE_TYPES.SPEED: cost = (int)UPGRADE_COSTS.SPEED; break;
                    case (int)UPGRADE_TYPES.POTENCY: cost = (int)UPGRADE_COSTS.POTENCY; break;
                    case (int)UPGRADE_TYPES.STATIC_POTENCY: cost = (int)UPGRADE_COSTS.STATIC_POTENCY; break;
                    case (int)UPGRADE_TYPES.OUTBREAK: cost = (int)UPGRADE_COSTS.OUTBREAK; break;
                    case (int)UPGRADE_TYPES.RESILIENCE: cost = (int)UPGRADE_COSTS.RESILIENCE; break;
                    case (int)UPGRADE_TYPES.BASE: cost = (int)UPGRADE_COSTS.BASE; break;
                }
                upgrades.Add(new Upgrade(cost));

            }

        }

        #endregion

        #region constructors

        public Virus(Tile t)
        {
            Console.WriteLine("Keegan is right, B*TCH!! :333");
            initializeUpgrades();
            tile = t;
            tile.plantVirus(this);
            updateVisibleTiles();
            allianceList = new List<int>();
        }

        public Virus(Tile t, int playerIndex) : this(t) {

            this.playerIndex = playerIndex; //need player index!
            addUpgrade((int)UPGRADE_TYPES.BASE);
            speed = (int)VIRUS_DEFAULTS.SPEED;
            resilient = false;
            outbreak = false;
            active = true;
        }
        public Virus(Virus v, Tile t) : this(t)
        {
            active = false;
            potency = 0;
            decayCopy(v);
        }

        public bool decayCopy(Virus v)
        {
            bool copied = false;
            if (!active && v.getRawPotency() - (int)VIRUS_DEFAULTS.POTENCY_MOD >= potency)
            {
                copied = true;
                playerIndex = v.getPlayerIndex();
                potency = v.getRawPotency() - (int)VIRUS_DEFAULTS.POTENCY_MOD;
                speed = (int)VIRUS_DEFAULTS.SPEED;
                outbreak = false;
                resilient = false;
                ageless = false;
            }
            return copied;
        }

        #endregion

        #region getters

        public static bool isFriend(int playerIndex, Virus v)
        {
            return (v.isAlly(playerIndex) || playerIndex == v.getPlayerIndex());
        }

        public static bool isFriend(Virus v1, Virus v2)
        {
            return isFriend(v1.getPlayerIndex(), v2);
        }

        public static int GET_BASE_COST(int fromPotency = 0)
        {
            /*
             *Base cost
             * p = starting potency
             * p_base = standard base potency
             * c = potency cost
             * b_c = potency cost after base
             * X_n = cost to upgrade to base, THEN upgrade potency
             * Y_n = cost to upgrade potency, THEN upgrade to base
             * if(p < p_base)
             *      X_p = 8*8
             *      Y_p = 6*p + 8*8
             *      advantage: upgrade to base first
             * if(p >= p_base)
             *      X_p = 8*8 + 16*(n-8) = 16*n - 64 = 16*(n - 4)
             *      Y_p = 6*n + 8*n = 14*n
             *      advantage: upgrade potency first
             */
            int baseCost = (int)UPGRADE_COSTS.BASE;
            int basePotency = (int)VIRUS_DEFAULTS.BASE_POTENCY;
            return baseCost * (fromPotency < basePotency ? basePotency : fromPotency);
        }

        public int getPlayerIndex() { return playerIndex; }
        public bool hasOutbreak() { return outbreak; }
        public int getSpeed() { return speed; }

        //used during attack, support and expansion
        public int getConflictPotency() { return (active ? (potency + staticPotency) : 0); }
        //used when viewing board, determining max upgrade limit
        public int getViewPotency() { return potency + staticPotency; }
        //used when upgrading, removing upgrade, aging
        public int getRawPotency() {return potency;}

        public bool isResilient() { return resilient; }
        public Tile getTile() { return tile; }
        public bool isAlive() {
            return (potency + staticPotency > 0) && (getTile() != null) && (getTile().getVirus() == this);
        
        }

        public bool isActive() { return active; }
        public void activate() { active = true; }

        public HashSet<Tile> getVisibleTiles() {return visibleTiles;}

        private bool isAlly(int playerIndex)
        {
            return allianceList.Contains(playerIndex);
        }
        public void updateAlliances(List<int> alliances)
        {
            this.allianceList = alliances;
        }

        #endregion

        public void setTile(Tile t)
        {
            this.tile = t;
        }

        #region automatic game logic

        public List<Virus> expand() {
            List<Virus> newViruses = new List<Virus>();
            if (potency > (int)VIRUS_DEFAULTS.POTENCY_MOD) newViruses = tile.expand();
            return newViruses;
        }

        private void updateVisibleTiles()
        {
            visibleTiles = tile.getVisibleTiles(speed - (int)VIRUS_DEFAULTS.SPEED + 2, outbreak);
        }


        //***********************************************************************Rick Alert Changes*************************************************************************
        /*public void tryExpand(Virus virtualVirus)
        {
            myVirtualVirus = virtualVirus;
            List<Tile> temp = new List<Tile>() ;
            if (potency > (int)VIRUS_DEFAULTS.POTENCY_MOD) temp = tile.tryExpand(virtualVirus);
            foreach (Tile t in temp)
            {
                underInfluence.Add(t);
            }
        }*/
        //***********************************************************************Rick Alert Changes*************************************************************************/

        public int CompareTo(Virus other)
        {
            int compareValue;
            if (other == null) compareValue = 1;
            else {
                compareValue = potency.CompareTo(other.getRawPotency());
                if (compareValue == 0)
                {
                    compareValue = outbreak.CompareTo(other.hasOutbreak());
                    if (compareValue == 0) return speed.CompareTo(other.getSpeed());
                }
            }
            return compareValue;
        }

        bool halfAged; //intermediate aging value for resilient viruses

        //if false, virus has died.
        public bool age() {
            bool stillAlive = true;
            tile.clearMarks();
            /*
             * This next line determines whether to "age" the virus.
             * The "halfAged" variable only matters if the virus is resilient.
             */
            halfAged = !halfAged;
            if (!tile.age())
            {
                kill();
                stillAlive = false;
            }
            else if(!(resilient && halfAged)) 
            {
                if (ageless) {
                    if (staticPotency >= (int)VIRUS_DEFAULTS.POTENCY_MOD)
                    {
                        staticPotency -= (int)VIRUS_DEFAULTS.POTENCY_MOD;
                        if (staticPotency % (int)VIRUS_DEFAULTS.STATIC_POTENCY_MOD == 0)
                            upgrades[(int)UPGRADE_TYPES.STATIC_POTENCY].remove();

                        potency += (int)VIRUS_DEFAULTS.POTENCY_MOD;
                    }
                }
                else if (staticPotency >= (int)VIRUS_DEFAULTS.POTENCY_MOD)
                {
                    staticPotency -= (int)VIRUS_DEFAULTS.POTENCY_MOD;
                    if (staticPotency % (int)VIRUS_DEFAULTS.STATIC_POTENCY_MOD == 0)
                        upgrades[(int)UPGRADE_TYPES.STATIC_POTENCY].remove();
                }
                else if (potency > (int)VIRUS_DEFAULTS.POTENCY_MOD)
                {
                    if(upgrades != null)
                        upgrades[(int)UPGRADE_TYPES.POTENCY].remove();
                    potency -= (int)VIRUS_DEFAULTS.POTENCY_MOD;
                }
                else
                {
                    kill();
                    stillAlive = false;
                }
            }

            return stillAlive;
            /*
            tile.clearMarks();

            if (!tile.age())
            {
                kill();
                return false;
            }

            updateVisibleTiles();
            if (resilient)
            {
                if (!halfAged) { 
                    halfAged = true; 
                    return true; 
                }
                else halfAged = false;
            }

            if (ageless) return true;
            if (staticPotency >= (int)VIRUS_DEFAULTS.POTENCY_MOD)
            {
                staticPotency -= (int)VIRUS_DEFAULTS.POTENCY_MOD;
                //this makes sure that static potency upgrades are internally consistent.
                if (staticPotency % (int)VIRUS_DEFAULTS.STATIC_POTENCY_MOD == 0)
                {
                    upgrades[(int)UPGRADE_TYPES.STATIC_POTENCY].remove();
                }
                return true;
            }
            else if (potency > (int)VIRUS_DEFAULTS.POTENCY_MOD)
            {
                upgrades[(int)UPGRADE_TYPES.POTENCY].remove();
                potency -= (int)VIRUS_DEFAULTS.POTENCY_MOD;
                return true;
            }
            else
            {
                kill();
                return false;
            }
             * */
        }

        public void kill()
        {
            staticPotency = 0;
            potency = 0;
            if (tile != null)
            {
                tile.plantVirus(null);
                tile = null;
            }
            /*foreach (Tile t in this.underInfluence)
            {
                //t.removeInfluence(this.playerIndex, myVirtualVirus);
            }*/
        }

    #endregion

        public override bool Equals(Object obj)
        {
            if (this.tile != null & ((Virus)obj).tile != null & this.getPlayerIndex() == ((Virus)obj).getPlayerIndex() && this.tile.Equals(((Virus)obj).tile))
                return true;
            else
                return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }//*/
    }
}
